为了账号安全,请及时绑定邮箱和手机立即绑定

Spring中的循环依赖

标签:
JavaScript

webp

circle

阅读原文请访问我的博客BrightLoong's Blog


什么是循环依赖

循环依赖就是循环引用,在spring中,就是两个或者多个bean相互之间持有对方。如下图,ClassA引用ClassB,ClassB引用ClassC,ClassC又引用ClassA,最终它们形成了一个环,这就是循环依赖。

webp

circle

Spring中的循环依赖

spring中将循环依赖分成了3中情况,分别是:

  • 构造器循环依赖

  • prototype范围的依赖处理

  • setter循环依赖

构造器循环依赖

通过构造器注入构成的循环依赖,此依赖无法解决。在Spring中会抛出BeanCurrentlyInCreationException异常表示循环依赖。

对于构造器注入构成的循环依赖,在创建ClassA的时候,构造器需要ClassB,然后去创建ClassB,在创建ClassB的时候发现需要ClassA,形成了一个死循环,无法完成创建。

prototype范围的依赖处理

对于prototype作用域的bean,spring容器无法完成依赖注入,因为spring不像缓存单例那样缓存prototype作用域的bean。

setter循环依赖

表示通过setter注入方式构成的循环依赖,spring通过提前暴露构造器注入但未完成其他步骤(如setter操作)的bean来完成setter注入造成的循环依赖。

自己简单的用代码来展示spring解决单例setter循环依赖的方式,具体spring中如何解决感兴趣可以自己阅读源码。

创建两个循环依赖的类,ClassA和ClassB。

package io.github.brightloong.lab.spring.cyclicdependence;/**
 * @author BrightLoong
 * @date 2018/9/13 11:17
 * @description
 */public class ClassA {    private ClassB classB;    public ClassB getClassB() {        return classB;
    }    public void setClassB(ClassB classB) {        this.classB = classB;
    }    public void say() {
        System.out.println("I am ClassA");
    }
}

package io.github.brightloong.lab.spring.cyclicdependence;/**
 * @author BrightLoong
 * @date 2018/9/13 11:17
 * @description
 */public class ClassB {    private ClassA classA;    public ClassA getClassA() {        return classA;
    }    public void setClassA(ClassA classA) {        this.classA = classA;
    }    public void say() {
        System.out.println("I am ClassB. Who are you?");
        classA.say();
    }
}

ObjectFactory用来模仿Spring解决循环依赖获取bean

package io.github.brightloong.lab.spring.cyclicdependence;import java.lang.reflect.Field;import java.util.Arrays;import java.util.Map;import java.util.concurrent.ConcurrentHashMap;/**
 * @author BrightLoong
 * @date 2018/9/13 11:19
 * @description
 */public class ObjectFactory {    /**用于缓存正在初始化中的对象,同时作为提前暴露的缓存*/
    private static final Map<Class, Object> currentInitObjects = new ConcurrentHashMap<>();    /**用于缓存初始化好的单例对象*/
    private static final Map<Class, Object> objects = new ConcurrentHashMap<>();    /**
     * 获取对象,并设值对象属性。
     * 1. 不考虑并发问题,简单的示例
     * 2. 解决单例setter循环依赖
     *
     * @param cls
     * @param <T>
     * @return
     */
    public <T> T getObject(Class<T> cls) {        //如果已经初始化过直接返回
        if (objects.containsKey(cls)) {            return (T) objects.get(cls);
        }        try {
            T t;            //1. 简单的使用构造函数创建对象,并提前暴露到currentInitObjects中
            t = cls.newInstance();            //提前暴露到currentInitObjects中
            currentInitObjects.put(cls, t);            //2. 解决依赖属性值
            resolveDependence(t, cls);            //3. 放入单例缓存中
            objects.put(cls, t);            return t;
        } catch (Exception e) {
            System.out.println("初始化对象失败:" + cls);            return null;
        } finally {            //4. 从正在初始化缓存中移除
            currentInitObjects.remove(cls);
        }
    }    /**
     * 解决依赖属性设值.
     * @param object 对象
     * @param cls 对象class
     */
    private void resolveDependence(Object object, Class cls) {        //获取对象的属性,并进行赋值,省去了复杂的判断,就认为是对象

        //1.获取所有属性
        Field[] fields = cls.getDeclaredFields();        //2.循环处理属性值
        Arrays.stream(fields).forEach(field -> {
            field.setAccessible(true);            //2.1 获取属性class属性
            Class fieldClass = field.getType();
            Object value;            //2.2 判断是否已经初始化过
            if (objects.containsKey(fieldClass)) {
                value = objects.get(fieldClass);
            } else if (currentInitObjects.containsKey(fieldClass)) {                //2.3 判断当前初始化的类中有没有这个属性.
                value = currentInitObjects.get(fieldClass);
            } else {                //2.4 如果都没有,进行初始化
                value = getObject(fieldClass);
            }            //3. 使用反射设置属性的值
            try {
                field.set(object, value);
            } catch (IllegalAccessException e) {
                System.out.println("设置对象属性失败:" + cls + "-" + field.getName());
            }
        });
    }
}

客户端调用

package io.github.brightloong.lab.spring.cyclicdependence;/**
 * @author BrightLoong
 * @date 2018/9/13 11:19
 * @description
 */public class Client {    public static void main(String[] args) {
        ObjectFactory factory = new ObjectFactory();
        ClassB classB = factory.getObject(ClassB.class);
        classB.say();
        System.out.println("-----我是分割线-----");

        ClassA classA = factory.getObject(ClassA.class);
        classA.say();
        System.out.println("classB.getClassA() == classA:" + (classB.getClassA() == classA));

        System.out.println("classA.getClassB() == classB:" + (classA.getClassB() == classB));
    }
}

输出如下:

I am ClassB. Who are you?
I am ClassA
-----我是分割线-----
I am ClassA
classB.getClassA() == classA:trueclassA.getClassB() == classB:true

从输出可以发现:

  • ClassA和ClassB都成功实例化

  • 都是单例



作者:BrightLoong
链接:https://www.jianshu.com/p/6d70a44ec720


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
JAVA开发工程师
手记
粉丝
205
获赞与收藏
1007

关注作者,订阅最新文章

阅读免费教程

  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消